<?php
/**
 * Mechanism
 *
 * LICENSE
 *
 * Copyright (c) 2011, Sergey A. Ivonchik <BreatheInMyVoid@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the following disclaimer
 *   in the documentation and/or other materials provided with the
 *   distribution.
 * * Neither the name of the  nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @package Mechanism
 * @license http://www.opensource.org/licenses/bsd-license
 * @author Sergey A. Ivonchik <BreatheInMyVoid@gmail.com>
 * @copyright Copyright (c) 2011 Sergey A. Ivonchik
 */

namespace Mechanism\Router\Route {
	/**
	 * UriTest
	 *
	 * Uri route test case. Here we testing
	 * abstract route class and do not repeat
	 * tests in child classes.
	 *
 	 * @package Mechanism\Router\Route
 	 * @author Sergey A. Ivonchik <BreatheInMyVoid@gmail.com>
	 */
	class UriTest extends \PHPUnit_Framework_TestCase {
		/**
		 * Route with default rules
		 *
		 * @var Mechanism\Router\Route\Uri
		 */
		protected $route = null;

		/**
		 * Setup
		 */
		public function setUp( ) {
			$this->route = new Uri( '[module]{s}[controller]{s}[action]' );
			$this->route->setDefault( 'module', 'main' )
						->setDefault( 'controller', 'index' )
						->setDefault( 'action', 'index' );
		}

		/**
		 * Test for constructor
		 *
		 * @test
		 */
		public function constructor( ) {
			try {
				$route = new Uri( '{s}[module]{s}[controller]{s}[action]' );
			} catch ( Exception\Invalid $e ) {
				/* Test constructor with defaults */
				$defaults = array(
					'module' => 'main',
					'controller' => 'index',
					'action' => 'index'
				);

				$defaultRoute = new Uri(
					'[module]{s}[controller]{s}[action]',
					$defaults
				);

				$this->assertEquals(
					$defaults,
					$defaultRoute->getDefault( )
				);

				return;
			}

			$this->fail('An expected Invalid exception has not been raised.');
		}

		/**
		 * Test for set( $name, $value )
		 * 
		 * @test
		 * @return Mechanism\Router\Route\Uri $this->route
		 */
		public function set( ) {
			/* Test set method and fluent interface of it */
			$this->assertSame(
				$this->route,
				$this->route->set( 'lang', 'ru' )
			);

			$this->assertSame(
				$this->route,
				$this->route->set( 'module', 'main' )
			);

			return $this->route;
		}

		/**
		 * Test for get( $name = null )
		 *
		 * @test
		 * @depends set
		 * @param Mechanism\Router\Route\Uri $route
		 */
		public function get( $route ) {
			$this->assertEquals(
				'ru',
				$route->get( 'lang' ),
				'lang property has not been setted'
			);
			
			/* Get unsetted */
			$this->assertEquals(
				null,
				$route->get( 'adsofaijdsfoi' )
			);

			/* Get all */
			$this->assertEquals(
				array(
					'lang' => 'ru',
					'module' => 'main'
				),
				$route->get( )
			);
		}

		/**
		 * Test for setDefault( $name, $value )
		 * 
		 * @test
		 * @return Mechanism\Router\Route\Uri $this->route
		 */
		public function setDefault( ) {
			/* Test setDefault method and fluent interface of it */
			$this->assertSame(
				$this->route,
				$this->route->setDefault( 'module', 'main' )	
			);		
			
			$this->assertSame(
				$this->route,
				$this->route->setDefault( 'controller', 'index' )	
			);	
			
			$this->assertSame(
				$this->route,
				$this->route->setDefault( 'action', 'index' )	
			);	
			
			return $this->route;
		}
		
		/**
		 * Test for getDefault( $name = null )
		 * 
		 * @test
		 * @depends setDefault
		 * @param Mechanism\Router\Route\Uri $route
		 */		
		public function getDefault( $route ) {
			/* Test single */
			$this->assertEquals(
				'index',
				$route->getDefault( 'action' )
			);				
			
			/* Get unsetted */
			$this->assertEquals(
				null,
				$route->getDefault( 'lkasdfhkjh' )
			);			
			
			/* Test return array */
			$setted = array( 
				'module' => 'main',
				'controller' => 'index',
				'action' => 'index'
			);
			
			$this->assertEquals(
				$setted,
				$route->getDefault( )
			);
		}

		/**
		 * Test for setRule( $param, $rule )
		 *
		 * @test
		 * @return Mechanism\Router\Route\Uri $this->route
		 */
		public function setRule( ) {
			/* Test setRule method and fluent interface of it */
			$this->assertSame(
				$this->route,
				$this->route->setRule( 'module', '[a-z]{5}' )
			);

			/* Test giving unset part of route */
			try {
				$this->route->setRule( 'lang', '[a-z]{2}' );
			} catch ( Exception\Runtime $e ) {
				return $this->route;
			}

			$this->fail('An expected Runtime exception has not been raised.');
		}

		/**
		 * Test for getRule( $param )
		 *
		 * @test
		 * @depends setRule
		 * @param Mechanism\Router\Route\Uri $route
		 */
		public function getRule( $route ) {
			$this->assertEquals(
				'[a-z]{5}',
				$route->getRule( 'module' )
			);

			/* Get unsetted */
			$this->assertEquals(
				null,
				$route->getRule( 'lkasdfhkjh' )
			);
		}

		/**
		 * Test for setSeparator( $separator )
		 *
		 * @test
		 * @return Mechanism\Router\Route\Uri $this->route
		 */
		public function setSeparator( ) {
			/* Test setSeparator method and fluent interface of it */
			$this->assertSame(
				$this->route,
				$this->route->setSeparator( '#' )
			);

			return $this->route;
		}

		/**
		 * Test for getSeparator( )
		 *
		 * @test
		 * @depends setSeparator
		 * @param Mechanism\Router\Route\Uri $route
		 */
		public function getSeparator( $route ) {
			/* Test defaults */
			$this->assertEquals(
				'/',
				$this->route->getSeparator( )
			);

			$this->assertEquals(
				'#',
				$route->getSeparator( )
			);
		}
		
		/**
		 * Test for match( $path )
		 *
		 * Test for default route
		 *
		 * @test
		 */
		public function match( ) {
			/* Valid data for test */
			$testUriA = 'http://www.example.com';
			$testUriAResult = array(
				'module' => 'main',
				'controller' =>'index',
				'action' =>'index'
			);
			
			$testUriB = '/blog/politics/show';
			$testUriBResult = array(
				'module' => 'blog',
				'controller' =>'politics',
				'action' =>'show'
			);

			$testUriC = '/blog/politics';
			$testUriCResult = array(
				'module' => 'blog',
				'controller' =>'politics',
				'action' =>'index'
			);

			/* Test valid A */
			$this->assertTrue(
				$this->route->match( $testUriA )
			);
			$this->assertEquals(
				$testUriAResult,
				$this->route->get( )
			);

			/* Test valid B */
			$this->assertTrue(
				$this->route->match( $testUriB )
			);
			$this->assertEquals(
				$testUriBResult,
				$this->route->get( )
			);

			/* Test valid C */
			$this->assertTrue(
				$this->route->match( $testUriC )
			);
			$this->assertEquals(
				$testUriCResult,
				$this->route->get( )
			);

			/* Test invalid */
			$this->assertFalse(
				$this->route->match( '/asdf/asdfa/asdfa/asdfsadf' )
			);			
		}

		/**
		 * Test for match with different routes
		 *
		 * @test
		 */
		public function uriRoute( ) {
			/* Static with parameters */
			$route = new Uri( '<this>{s}<is>{s}<static>' );
			$route->set( 'controller', 'work' )
				  ->set( 'action', 'index' );

			$this->assertFalse(
				$route->match( 'http://www.example.com/this/is/statik' )
			);
			$this->assertTrue(
				$route->match( 'http://www.example.com/this/is/static/' )
			);
			$this->assertEquals(
				array(
					'controller'=> 'work',
					'action' => 'index'
				),
				$route->get( )
			);

			/* With language */
			$langRoute = new Uri( '[lang]{s}[controller]{s}[action]');
			$langRoute->setDefault( 'lang', 'en' )
					  ->setDefault( 'controller', 'index' )
					  ->setDefault( 'action', 'index' )
					  ->setRule( 'lang', '[a-z]{0,2}' );

			$this->assertFalse(
				$langRoute->match( 'rus/my/docs' )
			);
			$this->assertTrue(
				$langRoute->match( 'http://www.example.com/' )
			);
			$this->assertEquals(
				array(
					'lang' => 'en',
					'controller'=> 'index',
					'action' => 'index'
				),
				$langRoute->get( )
			);

			/* Fun route */
			$funRoute = new Uri( '[action]{s}([^\s]*){s}[controller]' );
			$funRoute->setSeparator( ' ' )
					 ->setDefault( 'controller', 'index' )
					 ->setDefault( 'action', 'index' );


			$this->assertFalse(
				$funRoute->match( 'http://www.example.com/play with my ball' )
			);
			$this->assertTrue(
				$funRoute->match( 'http://www.example.com/play with ball' )
			);
			$this->assertEquals(
				array(
					'controller'=> 'ball',
					'action' => 'play'
				),
				$funRoute->get( )
			);

			/* One part without setted params and params in url*/
			$onePartRoute = new Uri( '<part>' );
			$onePartRoute->requireParams( );	
			
			$this->assertFalse(
				$onePartRoute->match( 'Part' )
			);
			
			/* Cause we do not specify default and url params...so there is no where to go */
			$this->assertFalse(
				$onePartRoute->match( '/part/' )
			);				
		}
		
		/**
		 * Test for requireParams( $flag = true )
		 * 
		 * @test
		 * @return Mechanism\Router\Route\Uri $router
		 */
		public function requireParams( ) {
			/* With required url params */
			$withoutParamsRoute = new Uri( '[username]' );
			$withoutParamsRoute->set( 'controller', 'users' )
							   ->set( 'action', 'get' );
							   
			/* Test for requireParams fluent interface */
			$this->assertSame(
				$withoutParamsRoute,
				$withoutParamsRoute->requireParams( )
			);				   
			
			return $withoutParamsRoute;
		}	

		/**
		 * Test for matching with required params
		 * 
		 * @test
		 * @depends requireParams
		 * @param Mechanism\Router\Route\Uri $route
		 */
		public function matchWithRequireParams( $route ) {
			/* Without params in url */				   
			$this->assertFalse(
				$route->match( 'http://www.example.com/' )
			);

			/* With params */
			$this->assertTrue(
				$route->match( 'http://www.example.com/someName' )
			);				
		}
	}
}